/*=====================================================
Critical Links
Auto Configuration Server

Authors:
	Rui Eduardo Gouveia Gil - r-gil@critical-links.com

Date: 2010
Version 0.1
=======================================================*/
#include <rgcpp/exceptions/ExceptionHandler.h>
#include <rgcpp/utils/LogHandler.h>
#include "database/DataBase.h"
#include "httpd/HttpServer.h"
#include "ACSConfig.h"
#include "ACServer.h"
#include <signal.h>

namespace acs
{

bool ACServer::shutdown = false;

ACServer::~ACServer()
{

}

void ACServer::start()
{
	if(this->daemon)
		this->goDaemon();

	LOG_INFO_STR("##### Starting  EA Content Server... #####");

	ACSConfig::checkSystemDirectory();

	DataBase::getInstance();
	HttpServer::getInstance();

	LOG_INFO_STR("#####  EA Content Server runnig !!! #####");
	if(this->daemon)
	{
		boost::mutex mut;
		while(true)
			mut.lock();
	}
	else
	{
		std::string command;
		std::vector<std::string> args;
		do
		{
			std::cin >> command;

		}while(!((command == "quit") || (command == "q")));

		this->stop();
	}
}

void ACServer::stop()
{
	LOG_INFO_STR("##### Stopping  EA Content Server... #####");

	HttpServer::killInstance();
	DataBase::killInstance();

	LOG_INFO_STR("#####  EA Content Server Stopped !!! #####");

	::exit(0);
}

void ACServer::restart()
{

	sleep(5);

}

void ACServer::printStatus()
{

}

bool ACServer::isDaemon() const
{
	return this->daemon;
}

void ACServer::sendStopSignal()
{
	::kill(ACServer::readPid(),SIGTERM);
}

void ACServer::sendRestartSignal()
{
	::kill(ACServer::readPid(),SIGUSR1);
}

void ACServer::sendStatusSignal()
{
	::kill(ACServer::readPid(),SIGUSR2);
}

void ACServer::installSignalHandlers()
{
	struct sigaction newh, oldh;
	newh.sa_handler=&ACServer::signalHandler;
	sigset_t sMask;
	sigemptyset(&sMask);

	newh.sa_flags=SA_RESTART|SA_NOMASK;
	newh.sa_mask=sMask;

	if(sigaction(SIGINT,&newh,&oldh)<0)
		LOG_FATAL_STR("Failed to install signal handler for SIGINT");

	if(sigaction(SIGHUP,&newh,&oldh)<0)
		LOG_FATAL_STR("Failed to install signal handler for SIGHUP");

	if(sigaction(SIGTERM,&newh,&oldh)<0)
		LOG_FATAL_STR("Failed to install signal handler for SIGTERM");

	if(sigaction(SIGQUIT,&newh,&oldh)<0)
		LOG_FATAL_STR("Failed to install signal handler for SIGQUIT");

	if(sigaction(SIGUSR1,&newh,&oldh)<0)
		LOG_FATAL_STR("Failed to install signal handler for SIGUSR1");

	if(sigaction(SIGUSR2,&newh,&oldh)<0)
		LOG_FATAL_STR("Failed to install signal handler for SIGUSR2");
}

void ACServer::signalHandler(int signal)
{
	if(signal!=SIGINT && signal!=SIGTERM && signal!=SIGUSR1 && signal!=SIGUSR2)
	{
		LOG_ERROR_DATA("expected SIGINT/SIGTERM, got:" << signal << ", returning");
		return;
	}

	switch(signal)
	{
		case SIGINT:
		case SIGHUP:
		case SIGTERM:
		case SIGQUIT:
		{
			if(ACServer::shutdown)
			{
				LOG_FATAL_STR("Aborting: process didn't die gracefuly, smashing it");
				exit(-1);
			}
			else
				ACServer::getInstance()->stop();

			ACServer::shutdown = true;
		}break;

		case SIGUSR1:
		{
			ACServer::getInstance()->restart();
		}break;

		case SIGUSR2:
		{
			ACServer::getInstance()->printStatus();
		}break;
	}
}

ACServer::ACServer(bool aDaemon) : daemon(aDaemon)
{
	if(!daemon)
	{
		std::ofstream file("/var/run/acs_server.pid",std::ios_base::binary | std::ios_base::out | std::ios_base::trunc);
		if(!file.good())
		{
			LOG_FATAL_STR("Invalid pid file path!!!");
			exit(-1);
		}

		file << ::getpid();
		file.close();
	}
}

void ACServer::goDaemon()
{
	if(::daemon(0,0) == -1)
		std::cerr << "ACServer::goDaemon(): error - " << strerror(errno) << std::endl;


	std::ofstream file("/var/run/acs_server.pid",std::ios_base::binary | std::ios_base::out | std::ios_base::trunc);
	if(!file.good())
	{
		LOG_FATAL_STR("Invalid pid file path!!!");
		exit(-1);
	}

	file << ::getpid();
	file.close();
}

pid_t ACServer::readPid()
{
	std::ifstream file("/var/run/acs_server.pid",std::ios_base::binary | std::ios_base::in);
	if(!file.good())
	{
		std::cout << "Invalid pid file path!!!" << std::endl;;
		exit(-1);
	}

	std::string str;

	file >> str;

	file.close();

	return atoi(str.c_str());
}

}

int main(int argc, char* argvs[])
{
	bool stop=false;
	bool detach=false;
	bool restart=false;
	std::string logFilePath;

	int opt;
	while((opt=::getopt(argc, argvs, "dsp:k:c:f:r:h")) != -1)
	{
		switch(opt)
		{
			case 'd':
			{
				detach=true;
			}break;

			case 's':
			{
				stop=true;
			}break;

			case 'r':
			{
				acs::ACSConfig::setSystemDirectory(optarg);
			}break;

			case 'k':
			{
				acs::ACSConfig::setPrivateKeyFile(optarg);
			}break;

			case 'c':
			{
				acs::ACSConfig::setCertificateFile(optarg);
			}break;

			case 'p':
			{
				acs::ACSConfig::setPort(atoi(optarg));
			}break;

			case 'f':
			{
				logFilePath = optarg;
			}break;

			case 'h':
			{
				std::cout << "ACServer Help" << std::endl;
				std::cout << "-h help" << std::endl;
				std::cout << "-d detach mode" << std::endl;
				std::cout << "-s stop daemon" << std::endl;
				std::cout << "-r repository path" << std::endl;
				std::cout << "-k private Key file path" << std::endl;
				std::cout << "-c certificate file path" << std::endl;
				std::cout << "-p http port" << std::endl;
				std::cout << "-f log configuration file" << std::endl;

				return 0;
			}break;

			case '?':
			{
				std::cout << "Invalid args: " << opt << std::endl;
				std::cout << "ACServer Help" << std::endl;
				std::cout << "-h help" << std::endl;
				std::cout << "-d detach mode" << std::endl;
				std::cout << "-s stop daemon" << std::endl;
				std::cout << "-r repository path" << std::endl;
				std::cout << "-k private Key file path" << std::endl;
				std::cout << "-c certificate file path" << std::endl;
				std::cout << "-p http port" << std::endl;
				std::cout << "-f log configuration file" << std::endl;

				return 0;
			}break;
		}
	}

	if(stop)
		acs::ACServer::sendStopSignal();
	else if(restart)
		acs::ACServer::sendRestartSignal();
	else
	{
		acs::ACServer::installSignalHandlers();

		rgcpp::ExceptionHandler::CreateExceptionHandler();
		if(logFilePath.empty())
		{

		}
		else
			rgcpp::LogHandler::getInstance(logFilePath);

		boost::shared_ptr<acs::ACServer> acsDaemon = acs::ACServer::getInstance(detach);
		acsDaemon->start();
	}

	return 0;
}
